package com.lintf.flowable.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.lintf.common.base.AjaxResult;
import com.lintf.common.exception.ServiceException;
import com.lintf.common.page.BaseController;
import com.lintf.common.page.TableDataInfo;
import com.lintf.flowable.domain.*;
import com.lintf.flowable.dto.WorkOrderDTO;
import com.lintf.flowable.service.*;
import com.lintf.flowable.token.RedisKeyConst;
import com.lintf.flowable.token.annotation.PassToken;
import com.lintf.flowable.token.user.UserContext;
import com.lintf.framework.config.redis.RedisCache;
import com.lintf.common.utils.string.StringUtils;
import com.lintf.flowable.dto.ProcessLaundryDTO;
import com.lintf.flowable.token.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author lin tingfeng
 * @ system Mac
 * @ data 2025/1/22 09:30
 * @ package_name com.lintf.flowable.controller
 * @ project_name sharding-sphere
 * @ desc
 */
@Slf4j
@RestController
@RequestMapping("/flowable/laundry")
public class FlowableLaundryController extends BaseController {

    private final RuntimeService runtimeService;
    private final TaskService taskService;
    private final SysUserService sysUserService;
    private final BasicStudentService basicStudentService;
    private final StudentSnService studentSnService;
    private final BasicOrderService basicOrderService;
    private final WorkOrderService workOrderService;
    private final RedisCache redisCache;

    private final String PROCESS_DEFINITION_KEY = "laundry";
    private final String BUSINESS_STATUS_DOING = "进行中";
    private final String BUSINESS_STATUS_COMPLETE = "待领取";
    private final String BUSINESS_STATUS_END = "已领取";
    private final String BUSINESS_STATUS_ERROR = "异常结束";

    @Autowired
    public FlowableLaundryController(RuntimeService runtimeService, TaskService taskService, SysUserService sysUserService, BasicStudentService basicStudentService, StudentSnService studentSnService, BasicOrderService basicOrderService, WorkOrderService workOrderService, RedisCache redisCache) {
        this.runtimeService = runtimeService;
        this.taskService = taskService;
        this.sysUserService = sysUserService;
        this.basicStudentService = basicStudentService;
        this.studentSnService = studentSnService;
        this.basicOrderService = basicOrderService;
        this.workOrderService = workOrderService;
        this.redisCache = redisCache;
    }

    /**
     * 获取用户列表
     *
     * @return
     */
    @GetMapping("/getUserList")
    @PassToken
    public TableDataInfo getUserList(SysUser sysUser) {
        return getDataTable(sysUserService.getUserList(sysUser));
    }

    @PostMapping("/checkUserInfo")
    @PassToken
    public AjaxResult checkUserInfo(@RequestBody SysUser sysUser) {
        SysUser user = sysUserService.getById(sysUser.getId());
        if (user == null) {
            throw new ServiceException("用户信息不存在");
        }
        if (!StringUtils.equals(user.getPassword(), sysUser.getPassword())) {
            throw new ServiceException("用户密码错误");
        }
        String token = redisCache.getCacheObject(RedisKeyConst.App.APP_TOKEN + user.getId());
        if (StringUtils.isNotEmpty(token)) {
            return AjaxResult.successByData(token);
        }
        token = TokenUtils.generateToken(user);
        redisCache.setCacheObject(RedisKeyConst.App.APP_TOKEN + user.getId(), token, 7 * 24 * 60 * 60, TimeUnit.SECONDS);
        return AjaxResult.successByData(token);
    }

    @GetMapping("/logout")
    public AjaxResult logout() {
        Integer userId = UserContext.getUser().getUserId();
        UserContext.clear();
        redisCache.deleteObject(RedisKeyConst.App.APP_TOKEN + userId);
        return success();
    }

    @GetMapping("/getUserInfo")
    public AjaxResult getUserInfo() {
        Integer userId = UserContext.getUser().getUserId();
        SysUser user = sysUserService.getById(userId);
        if (user == null) {
            throw new ServiceException("用户信息不存在");
        }
        LambdaQueryWrapper<WorkOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WorkOrder::getUserId, userId);
        List<WorkOrder> workOrderList = workOrderService.list(wrapper);
        long doingCount = workOrderList.stream().filter(workOrder -> StringUtils.equals(workOrder.getBusinessStatus(), BUSINESS_STATUS_DOING)).count();
        long completeCount = workOrderList.stream().filter(workOrder -> StringUtils.equals(workOrder.getBusinessStatus(), BUSINESS_STATUS_COMPLETE)).count();
        long endCount = workOrderList.stream().filter(workOrder -> StringUtils.equals(workOrder.getBusinessStatus(), BUSINESS_STATUS_END)).count();
        long totalCount = completeCount + endCount;
        long completeTime = workOrderList.stream().filter(workOrder -> workOrder.getCompleteTime() != null).mapToLong(WorkOrder::getCompleteTime).sum() / 1000 / 60;
        Map<String, Object> params = new HashMap<>();
        params.put("doingCount", doingCount);
        params.put("completeCount", completeCount);
        params.put("endCount", endCount);
        params.put("totalCount", totalCount);
        params.put("completeTime", completeTime);
        user.setParams(params);
        return success(user);
    }

    /**
     * 获取工单列表
     *
     * @return
     */
    @GetMapping("/getWorkOrderList")
    public TableDataInfo getWorkOrderList(WorkOrderDTO dto) {
        return getDataTable(workOrderService.getWorkOrderList(dto));
    }

    @GetMapping("/getWorkOrderListByParams")
    public TableDataInfo getWorkOrderListByParams(WorkOrderDTO dto) {
        return getDataTable(workOrderService.getWorkOrderListByParams(dto));
    }

    @GetMapping("/getStatistics")
    public AjaxResult getStatistics() {
        Map<String, Object> statistics = workOrderService.getStatistics();
        return success(statistics);
    }

    @PostMapping("/startProcess")
    public AjaxResult startProcess(@RequestBody ProcessLaundryDTO dto) {
        LambdaQueryWrapper<StudentSn> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StudentSn::getSerialNumber, dto.getSerialNumber());
        StudentSn studentSn = studentSnService.getOne(wrapper);
        if (studentSn == null) {
            throw new ServiceException("此NFC芯片没有绑定学生信息");
        }
        Integer studentId = studentSn.getStudentId();
        Integer userId = UserContext.getUser().getUserId();
        checkInfo(studentId, userId);
        WorkOrder workOrder = workOrderService.getWorkOrderByOne(userId, studentId, BUSINESS_STATUS_DOING);
        if (workOrder != null) {
            throw new ServiceException("存在未完成的工单流程");
        }
        Map<String, Object> variables = new HashMap<>();
        variables.put("userId", userId);
        variables.put("studentId", studentId);
        variables.put("assignee", String.valueOf(userId));
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(PROCESS_DEFINITION_KEY, variables);
        runtimeService.updateBusinessStatus(processInstance.getId(), BUSINESS_STATUS_DOING);
        dto.setProcessInstanceId(processInstance.getId());
        dto.setStudentId(studentId);
        dto.setUserId(userId);
        workOrderService.saveWorkOrder(dto);
        log.info("流程开始；实例ID：{}；流程状态：{}", processInstance.getId(), BUSINESS_STATUS_DOING);
        return AjaxResult.successByData(processInstance.getId());
    }

    @PostMapping("/completeProcess")
    @PassToken
    public AjaxResult completeProcess(ProcessLaundryDTO dto) {
        Integer userId = dto.getUserId();
        Integer studentId = dto.getStudentId();
        WorkOrder workOrder = workOrderService.getWorkOrderByOne(userId, studentId, BUSINESS_STATUS_DOING);
        if (workOrder == null) {
            throw new ServiceException("查询无工单信息");
        }
        String processInstanceId = workOrder.getProcessInstanceId();
        Task task = null;
        Map<String, Object> variables = new HashMap<>();
        variables.put("userId", userId);
        variables.put("studentId", studentId);
        variables.put("assignee", String.valueOf(studentId));
        try {
            log.info("流程实例ID：" + processInstanceId);
            task = taskService.createTaskQuery()
                    .processInstanceId(processInstanceId)
                    .taskAssignee(String.valueOf(userId))
                    .singleResult();
            if (task == null) {
                throw new ServiceException("工单流程不存在");
            }
            variables.put("result", true);
            long completeTime = new Date().getTime() - workOrder.getCreateTime().getTime();
            runtimeService.updateBusinessStatus(processInstanceId, BUSINESS_STATUS_COMPLETE);
            taskService.complete(task.getId(), variables);
            workOrderService.updateBusinessStatusByProcessInstanceId(processInstanceId, BUSINESS_STATUS_COMPLETE, completeTime);
            log.info("流程开始；实例ID：{}，流程状态：{}", processInstanceId, BUSINESS_STATUS_COMPLETE);
            return success();
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            variables.put("result", false);
            runtimeService.updateBusinessStatus(processInstanceId, BUSINESS_STATUS_ERROR);
            taskService.complete(task.getId(), variables);
            workOrderService.updateBusinessStatusByProcessInstanceId(processInstanceId, BUSINESS_STATUS_ERROR, null);
            log.error("流程异常；实例ID：{}，流程状态：{}", processInstanceId, BUSINESS_STATUS_ERROR);
            log.error("流程异常", e);
            return error("流程异常结束");
        }
    }

    @PostMapping("/receiveProcess")
    @PassToken
    public AjaxResult receiveProcess(ProcessLaundryDTO dto) {
        Integer userId = dto.getUserId();
        Integer studentId = dto.getStudentId();
        WorkOrder workOrder = workOrderService.getWorkOrderByOne(userId, studentId, BUSINESS_STATUS_COMPLETE);
        if (workOrder == null) {
            throw new ServiceException("查询无工单信息");
        }
        String processInstanceId = workOrder.getProcessInstanceId();

        Task task = null;
        Map<String, Object> variables = new HashMap<>();
        variables.put("studentId", studentId);
        variables.put("assignee", String.valueOf(studentId));
        try {
            log.info("流程实例ID：" + processInstanceId);
            task = taskService.createTaskQuery()
                    .processInstanceId(processInstanceId)
                    .taskAssignee(String.valueOf(studentId))
                    .singleResult();
            if (task == null) {
                throw new ServiceException("工单流程不存在");
            }
            variables.put("result", true);
            runtimeService.updateBusinessStatus(processInstanceId, BUSINESS_STATUS_END);
            taskService.complete(task.getId(), variables);
            workOrderService.updateBusinessStatusByProcessInstanceId(processInstanceId, BUSINESS_STATUS_END, null);
            log.info("流程开始；实例ID：{}，流程状态：{}", processInstanceId, BUSINESS_STATUS_END);
            return success();
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            variables.put("result", false);
            runtimeService.updateBusinessStatus(processInstanceId, BUSINESS_STATUS_ERROR);
            taskService.complete(task.getId(), variables);
            workOrderService.updateBusinessStatusByProcessInstanceId(processInstanceId, BUSINESS_STATUS_ERROR, null);
            log.error("流程异常；实例ID：{}，流程状态：{}", processInstanceId, BUSINESS_STATUS_ERROR);
            log.error("流程异常", e);
            return error("流程异常结束");
        }
    }

    private void checkInfo(Integer studentId, Integer userId) {
        if (userId != null) {
            getUserById(userId);
        }
        if (studentId != null) {
            getStudentById(studentId);
        }
    }

    private void getUserById(Integer userId) {
        SysUser user = sysUserService.getById(userId);
        if (user == null) {
            throw new ServiceException("用户信息不存在");
        }
    }

    private void getStudentById(Integer studentId) {
        BasicStudent student = basicStudentService.getById(studentId);
        if (student == null) {
            throw new ServiceException("学生信息不存在");
        }
        getOrderByStudentId(studentId);
    }

    private void getOrderByStudentId(Integer studentId) {
        Date date = new Date();
        long time = date.getTime();
        log.info("当前时间：{}", date);
        log.info("当前时间戳：{}", time);
        LambdaQueryWrapper<BasicOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BasicOrder::getStudentId, studentId);
        queryWrapper.le(BasicOrder::getOpenDate, date);
        queryWrapper.ge(BasicOrder::getEffectiveDate, date);
        BasicOrder basicOrder = basicOrderService.getOne(queryWrapper);
        if (basicOrder == null) {
            throw new ServiceException("学生尚未开通权益");
        }
    }
}
